/*
* 构造函数: Swiper
* 参数:
*       selector        //选择器字符串 或 元素对象 , 表示轮播图容器
*       options{
*           auto        是否自动播放 , 默认值 true
*           loop        是否无缝滚动 , 默认值 true
*           time        轮播时间间隔
*           paginations 是否生成导航点 , 默认值 true
*       }         //对象 , 封装轮播参数
* 依赖: transformCSS.js
* 使用示例: new Swiper('#swiper-container');
*           new Swiper(obj);
*           或者 new Swiper('#swiper-container',{
*                   auto: false,            //是否自动播放 , 默认为 true
*                   loop: false,            //是否无缝滚动 , 默认为 true
*                   time: 5000,             //轮播时间间隔
*                   paginations: false       //是否生成导航点 , 默认为 true
*                   callback: {             //回调函数
*                       end: function(){
*                           console.log("end");
*                       }
*                       move: function(){
*                           console.log("move");
*                       }
*                   }
*               })
* 存在问题:
*       必须先设置样式 引入 swiper.less 文件
*       选择器类名不能改变
*       初始化时直接绑定事件容易被覆盖
* */
(function(w){
    function Swiper(selector,options){
        // options 参数初始化
        //设置 options 选项
        /*var auto = true;
        if(options){
            if(options.auto === undefined){
                auto = true;
            }else{
                auto = options.auto;
            }
        }
        var loop = true;
        if(options){
            if(options.loop === undefined){
                loop = true;
            }else{
                loop = options.loop;
            }
        }
        var time = 2000;
        if(options){
            if(options.time === undefined){
                time = 2000;
            }else{
                time = options.time;
            }
        }
        var paginations = true;
        if(options){
            if(options.paginations === undefined){
                paginations = true;
            }else{
                paginations = options.paginations;
            }
        }*/
        var auto = options && options.auto !== undefined ? options.auto : true;
        var loop = options && options.loop !== undefined ? options.loop : true;
        var time = options && options.time !== undefined ? options.time : 2000;
        var paginations = options && options.paginations !== undefined ? options.paginations : true;

        //获取元素
        var container = null;
        //判断第一个参数是选择器还是元素对象
        if(typeof selector === 'object'){
            container = selector;
        }
        if(typeof selector === 'string'){
            container = document.querySelector(selector);
        }
        if(container === null){
            return console.error("容器不能为空");
        }

        var wrap=container.querySelector(".swiper-wrap");
        var pagination=container.querySelector(".swiper-pagination");
        var dots=container.querySelectorAll(".swiper-pagination span");
        var slides=container.querySelectorAll(".swiper-wrap .swiper-slide");

        //全局变量 / 常量
        //创建变量 index 保存图片的索引,初始值为 0
        var index=0;
        //获取当前 slide 的个数
        var len=slides.length;
        //获取屏幕宽度
        var W=container.clientWidth;


        /*
        * 初始化css样式:
        *       container 设为相对定位
        *       container的高度
        *       每个slide的宽度
        *       wrap的宽度
        *       根据图片个数添加导肮点
        * */
        container.init = function (){
            //container 设为相对定位
            container.style.position='relative';
            //设置container的高度=slides的高度
            /*
            * slides的高度由img撑开,但是img属于外部资源,浏览器加载需要时间,而代码执行速度比图片的加载速度要快,所以这里设置高度不成功
            * 要在 图片全部加载完毕之后再设置container的高度
            * 而且这里不能用 jQuery 中的 $(function(){}) ==> 文档结构加载完毕
            *                       window.onload ==> 文档的所有资源加载完毕
            * */
            window.addEventListener("load",function(){
                container.style.height=slides[0].offsetHeight +'px';
            });
            //设置每个slide的宽度
            /*
            * forEach()方法:
            *       遍历数组
            *       传递回调函数为参数,则数组有几个元素回调函数就会执行几次
            *       回调函数可以传三个参数: JS的执行标准中,这三个参数会自动封装对应的内容
            *               第一个参数 --> 对应每个元素 value
            *               第二个参数 --> 对应元素的索引 index
            *               第三个参数 --> 对应当前数组 now
            * */
            slides.forEach(function(slide){
                slide.style.width=container.clientWidth +'px';
            });
            //设置wrap的宽度,注意要先设置每个slide的宽度
            wrap.style.width=container.clientWidth * len +'px';
            //判断是否需要创建导航点
            if(paginations){
                //根据图片个数添加导航点
                for(var i=0;i<len;i++){
                    //创建span
                    var spanNode=document.createElement("span");
                    //为第一个导航点添加 active 类
                    if(i==0){
                        spanNode.classList.add("active");
                    }
                    //将spanNode添加到pagination中
                    pagination.appendChild(spanNode);
                }
                //重新获取所有导航点 dots
                dots=pagination.querySelectorAll("span");
            }

        }
        container.init();

        /*
        * 无缝滚动:
        *       复制所有图片 , 添加到 wrap 末尾
        *       获取所有图片的个数 length , 也是轮播中的实际图片个数
        * */
        //复制图片
        if(loop){
            wrap.innerHTML += wrap.innerHTML;
        }
        //重新获取所有图片slides
        slides= wrap.querySelectorAll(".swiper-slide");
        var length=slides.length;
        //重新设置 wrap 的宽度
        wrap.style.width=container.clientWidth * length +'px';

        /*
        * 自动播放:
        *       创建定时器的全局变量 timer
        *       创建自动播放函数 autoRun
        * 注意: 在定时器里 , 平常的边界控制方法不起作用
        *       要在 动画过渡完毕事件中控制边界:
        *               为元素绑定 transitionend 动画过渡结束事件 , 在每一次动画结束之后触发
        * */
        var timer = null;
        container.autoRun = function (){
            //判断是否需要自动播放
            if(!auto) return;

            //先关闭上一个定时器
            clearInterval(timer);
            //设置定时器
            timer = setInterval(function(){
                //设置过渡效果
                // wrap.style.transition="all 0.5s";
                index ++;
                //控制索引边界
                //含有过渡效果地切换
                container.switchSlide(index);
            },time);
        }
        container.autoRun();
        //只能在 transitionend 事件中控制定时器中的索引边界
        /*
        * transitionend 事件 , 在元素的每次过渡动画执行完毕后触发此事件
        * 要为执行过渡动画的元素绑定 transitionend 事件
        * */
        wrap.ontransitionend = function(){
            //判断是否是无缝滚动
            if(loop){
                //需要无缝滚动
                //判断: 如果是最后一张图片(index == length-1) , 则修改 index =4 (length/2 -1 或 len-1) , 修改 wrap 的 left 值
                if(index == length-1){
                    //取消过渡效果
                    // wrap.style.transition = "none";
                    index = length/2 -1;
                    //取消过渡效果地切换
                    container.switchSlide(index,false);
                }
            }
        };



        /*
        * 触摸事件
        * */
        //触摸开始事件
        container.addEventListener("touchstart",function(e){
            //取消过渡效果
            // wrap.style.transition="none";
            //关闭自动播放
            clearInterval(timer);
            //获取触摸开始的时间
            this.touchstartTime=Date.now();
            //获取触摸开始时的触点位置
            this.touchX=e.changedTouches[0].clientX;
            this.touchY=e.changedTouches[0].clientY;
            //衔接效果
            /*
            * 设置无缝衔接效果:
            *       触摸开始时判断: 如果是第一张图片(index == 0) , 则修改 index = (length/2 或 len) , 修改 wrap 的 left 值
            *                    如果是最后一张图片(index == length-1) , 则修改 index = (length/2 -1 或 len-1) , 修改 wrap 的 left 值
            *                    注意将获取 wrap 的 left 值放到衔接效果之后
            * */
            //判断是否需要无缝滚动
            if(loop){
                if(index == 0){
                    index = length/2;
                    //取消过渡效果地切换
                    container.switchSlide(index,false);
                }else if(index == length-1){
                    index = length/2 -1;
                    //取消过渡效果地切换
                    container.switchSlide(index,false);
                }
            }

            /*//获取wrap的left值
            this.wrapL=wrap.offsetLeft;*/

            //获取 wrap 的 translateX 的值
            this.wrapL=transformCSS(wrap,'translateX');
        });

        //触摸滑动事件
        //水平 / 垂直滑动的标识变量
        var isHori = true; //是水平滑动
        var isFirst = true; //是第一次滑动
        container.addEventListener("touchmove",function(e){
            //取消过渡效果
            wrap.style.transition="none";
            //获取触点位置
            this.touchX2=e.changedTouches[0].clientX;
            this.touchY2=e.changedTouches[0].clientY;
            /*
            * 滑动方向优化:
            *        判断滑动方向 , 水平 / 垂直
            *        水平滑动时 , 阻止向上滑动 (阻止浏览器默认事件)
            *        垂直滑动时 , 阻止水平滑动
            * 问题: 如果直接根据 水平 / 垂直 滑动距离判断滑动方向
            *       第一次滑动 垂直方向 > 水平方向 , 为垂直滑动 , 不能阻止浏览器默认事件 , 浏览器生成滚动条
            *       第二次滑动 水平方向 > 垂直方向 , 为水平滑动 , 需要阻止浏览器默认事件 , 阻止浏览器生成滚动条
            *       当第二次滑动的水平方向距离 > 第一次滑动的垂直方向距离时
            *       此时浏览器已经生成滚动条并进入第一次的滚动状态 , 但第二次滑动要阻止浏览器生成滚动条
            *       因此 , 浏览器报错:
            *           ( [Intervention] Ignored attempt to cancel a touchmove event with cancelable=false, for example because scrolling is in progress and cannot be interrupted. )
            * 解决: 只在第一次滑动时判断方向
            *       添加标识变量 isHori 和 isFirst , 初始值为 true , 在第一次滑动时修改 isFirst 的值为 false , 并且判断滑动方向
            * 注意: 记得要在触摸结束后为标识变量重新赋值 , 否则滑动事件只有第一次生效
            *       且在 touchend 事件中 , 如果是垂直滑动 , 则不应该修改 left 值和 index 值
            * */
            var disX = Math.abs(this.touchX2 -this.touchX);
            var disY = Math.abs(this.touchY2 -this.touchY);
            //当前为第一次滑动 , 将标识变量赋值为假
            if(isFirst){
                //修改标识变量
                isFirst = false;
                //第一次滑动时判断方向
                if(disX > disY){
                    //水平滑动
                    isHori = true;
                }else{
                    //垂直滑动
                    isHori = false;
                }
            }
            //根据滑动方向决定是否阻止默认行为
            if(isHori){
                //水平滑动
                e.preventDefault();
            }else{
                //垂直滑动
                return ;
            }
            //计算 wrap 应该的 left值
            var l=this.wrapL+(this.touchX2-this.touchX);
            /*//设置 wrap 的 left 值
            wrap.style.left=l +'px';*/

            //设置 wrap 的 translateX 的值
            transformCSS(wrap,'translateX',l);

            //执行 move 回调函数
            /*if(options){
                if(options.callback){
                    if(typeof options.callback.move === "function"){
                        options.callback.move();
                    }
                }
            }*/
            options && options.callback && typeof options.callback.move === "function" && options.callback.move();
        });

        //触摸结束事件
        container.addEventListener("touchend",function(e){
            //设置滑动过渡效果
            // wrap.style.transition="all 0.5s";
            //开启自动播放
            container.autoRun();
            //为滑动方向标识变量重新赋值
            isHori = true;
            isFirst = true;
            //获取触点位置
            this.touchX3=e.changedTouches[0].clientX;
            /*
            * 滑动检测: 时间检测 距离检测
            * (距离检测 Math.abs(this.touchX3-this.touchX) > container.offsetWidth/2
            *  时间检测 this.touchendTime-this.touchstartTime < 300)
            * */
            //获取触摸结束的时间
            this.touchendTime=Date.now();
            //判断是否是垂直滑动
            if(!isHori){
                //垂直滑动时 , 不修改 left 值和 index
                return ;
            }
            if(Math.abs(this.touchX3-this.touchX) > container.offsetWidth/2 || this.touchendTime-this.touchstartTime <= 300){
                //判断滑动方向: 向左 / 向右
                if(this.touchX3 < this.touchX){
                    //向左滑动
                    index ++;
                }else if(this.touchX3 > this.touchX){
                    //向右滑动
                    index --;
                }
            }
            //索引边界
            if(index<0){
                index=0;
            }else if(index>=length-1){
                index=length-1;
            }
            //含有过渡效果地切换
            container.switchSlide(index);
        });


        /*
        * 创建切换幻灯片的函数
        * 功能:
        *       1. 计算 left 值
        *       2. 设置 wrap 的 left 值
        *       3. 切换导航点
        * 参数:
        *       i               表示索引
        *       isTransition    表示是否过渡切换 , 不写为默认值 true
        * */
        //创建切换幻灯片的函数
        container.switchSlide = function (i,isTransition){
            //如果没有传递第二个参数 , 则默认为需要过渡
            if(isTransition === undefined){
                isTransition = true;
            }
            if(isTransition){
                //需要过渡
                wrap.style.transition = 'all 0.5s';
            }else{
                //不需要过渡
                wrap.style.transition = 'none';
            }
            //计算left值
            var l = -i*container.clientWidth;
            /*//设置wrap的left值
            wrap.style.left =l +'px';*/

            //设置 wrap 的 translateX 的值
            transformCSS(wrap,'translateX',l);
            //判断是否有导航点
            if(paginations){
                //切换导航点
                //移除所有导航点上的active类
                dots.forEach(function(dot){
                    dot.classList.remove("active");
                });
                //为当前 index 的导航点设置 active 类 , 索引值为对导航点数量取余
                dots[i % dots.length].classList.add("active");
            }
            //更新图片索引
            index = i;
            //执行 end 回调函数
            /*if(options){
                if(options.callback){
                    if(typeof options.callback.end === "function"){
                        options.callback.end();
                    }
                }
            }*/
            options && options.callback && typeof options.callback.end === "function" && options.callback.end();
        }


        //将 switchSlid 函数存入到 Swiper 中 , this 指向新创建的对象
        this.container = container;
        //返回当前显示的幻灯片的下标
        this.getIndex = function(){
            return index;
        }
    }
    w.Swiper = Swiper;
})(window);